﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using common;
using Utem.Command;

namespace Utem.Server
{
    public partial class FormControl : Form
    {
        private readonly ServerConnectionInfo _serverConnectionInfo = FileUtils.GetConnectionInfo();
        private List<PlayerInfo> _playerInfo = new List<PlayerInfo>();
        private readonly List<TextBox> _ipList = new List<TextBox>();
        private readonly List<TextBox> _nameList = new List<TextBox>();
        private readonly List<TextBox> _markList = new List<TextBox>();
        private List<QuestionManagement> _questionManagements = new List<QuestionManagement>();
        private List<int> _marks = new List<int>();
        private int[] _inCorrectPos = new int[6];
        private int[] _markStore = new int[6];

        public delegate void PassDataDelegate(string text);

        public delegate void PassQuestionDelegate(Question question);

        public delegate void PassStartDelegate(int sec);

        public delegate void PassStopDelegate();

        public delegate void PassBonusDelegate(int secs, bool type);

        public delegate void PassMarkDelegate(int id, int mark);

        public delegate void PassWinnerLoserDelegate(int id, string type);

        public delegate void PassInCorrectDelegate(params int[] pos);

        public delegate void PassCorrectAnswerDelegate(char answer);

        public PassDataDelegate PassData { get; set; }
        public PassQuestionDelegate PassQuestion { get; set; }
        public PassStartDelegate PassStart { get; set; }
        public PassStopDelegate PassStop { get; set; }
        public PassBonusDelegate PassBonus { get; set; }
        public PassMarkDelegate PassMark { get; set; }
        public PassWinnerLoserDelegate PassWinnerLoser { get; set; }
        public PassInCorrectDelegate PassInCorrect { get; set; }
        public PassCorrectAnswerDelegate PassCorrectAnswer { get; set; }


        private void LoadData()
        {
            #region load to list
            // Load to Ip list.
            _ipList.Add(txtIp1);
            _ipList.Add(txtIp2);
            _ipList.Add(txtIp3);
            _ipList.Add(txtIp4);
            _ipList.Add(txtIp5);
            _ipList.Add(txtIp6);

            // Load to Name list.
            _nameList.Add(txtPlayerName1);
            _nameList.Add(txtPlayerName2);
            _nameList.Add(txtPlayerName3);
            _nameList.Add(txtPlayerName4);
            _nameList.Add(txtPlayerName5);
            _nameList.Add(txtPlayerName6);

            // Load to Mark list.
            _markList.Add(txtMark1);
            _markList.Add(txtMark2);
            _markList.Add(txtMark3);
            _markList.Add(txtMark4);
            _markList.Add(txtMark5);
            _markList.Add(txtMark6);
            #endregion

        }

        private void FillPlayerInfo()
        {
            for (var i = 0; i < _playerInfo.Count; i++)
            {
                _ipList[i].Text = _playerInfo[i].Ip + @":" + _playerInfo[i].Port;
                _nameList[i].Text = _playerInfo[i].Name;
                _markList[i].Text = _playerInfo[i].Mark.ToString(CultureInfo.InvariantCulture);
            }
        }

        private static void ResetAllFields()
        {

        }

        private void ActionController(string action, string clientIp)
        {
            // Get 3 first characters.
            var temp = action.Substring(0, 3);
            string[] strs;
            switch (temp)
            {
                case "201":
                    strs = action.Split(':');
                    WriteToLog(String.Format("{0}", strs[1]));
                    break;
                case "205":
                    strs = action.Split(':');
                    WriteToLog(String.Format("{0} (ID = {1}): {2}", clientIp, strs[2], strs[1]));
                    var id = Int32.Parse(strs[2]);
                    Mark(id, strs[1]);
                    if (PassData != null)
                    {
                        PassData(action);
                    }
                    break;
                case "310":
                    ShowHideBonusAction(false);
                    btnStart.Text = @"Start";
                    strs = action.Split(':');
                    WriteToLog(String.Format("{0} ({1}) finish.", strs[1], clientIp));
                    break;
                case "404":
                    strs = action.Split(':');
                    WriteToLog(String.Format("{0} ({1}) throws exception: {2}", strs[1], clientIp, strs[2]));
                    break;
                default:
                    WriteToLog(action);
                    break;
            }

        }

        public void Mark(int id, string strSelected)
        {
            var selected = 0;
            switch (strSelected)
            {
                case "B":
                    selected = 1;
                    break;
                case "C":
                    selected = 2;
                    break;
                case "D":
                    selected = 3;
                    break;
            }
            if (selected == _questionManagements[listBoxQuestion.SelectedIndex].Correct)
            {
                _marks[id] = 1;
                _inCorrectPos[id] = 0;
            }
            else
            {
                _inCorrectPos[id] = 1;
                _marks[id] = 0;
            }
        }

        public void Mark()
        {
            for (var i = 0; i < _marks.Count; i++)
            {
                try
                {
                    _markList[i].Text = String.Format("{0}", (Int32.Parse(_markList[i].Text) + _marks[i]));
                    _markStore[i] += _marks[i];
                }
                catch (Exception ex)
                {
                    WriteToLog(ex.Message);
                }
            }
        }

        public FormControl()
        {
            InitializeComponent();
        }

        private void FormControl_Load(object sender, EventArgs e)
        {
            var ts = new ThreadStart(StartListen);
            var th = new Thread(ts);
            th.Start();
            ResetAllFields();
            LoadData();
            WriteToLog("Application start successfully...");
            ShowHideBonusAction(false);


        }

        private void StartListen()
        {
            var listener = new TcpListener(_serverConnectionInfo.Address, _serverConnectionInfo.Port);
            listener.Start();
            while (true)
            {
                var st = new StreamReader(listener.AcceptTcpClient().GetStream());
                CheckForIllegalCrossThreadCalls = false;
                var clientIp = IPAddress.Parse(((IPEndPoint)listener.LocalEndpoint).Address.ToString());
                ActionController(st.ReadToEnd(), clientIp.ToString());
                st.Close();
            }
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            PassData("199");
            for (var i = 0; i < _markStore.Count(); i++)
            {
                _markStore[i] = 0;
            }
            for (var i = 0; i < _ipList.Count; i++)
            {
                try
                {
                    if (String.IsNullOrEmpty(_nameList[i].Text)) continue;
                    _marks.Add(0);
                    var temp = _ipList[i].Text.Split(':');
                    var client = new TcpClient(temp[0], Int32.Parse(temp[1]));
                    var sw = new StreamWriter(client.GetStream());
                    sw.Write("200:{0}:{1}", _nameList[i].Text, i);
                    PassData(string.Format("200:{0}:{1}", _nameList[i].Text, i));
                    sw.Flush();
                    sw.Close();
                }
                catch (Exception ex)
                {
                    WriteToLog(ex.Message + "\n");
                }
            }
            txtLog.AppendText("Connect complete.\n");
        }

        private void listBoxPlayer_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (MessageBox.Show(@"Are you sure to load new data?", @"Warnning", MessageBoxButtons.YesNo,
                MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) != DialogResult.Yes) return;
            try
            {
                switch (listBoxPlayer.SelectedIndex)
                {
                    case 0: // Select part I
                        _playerInfo = FileUtils.GetPlayer("partI.txt");
                        FillPlayerInfo();
                        LoadQuestionFromFile(@"data\QuestionPartI.txt");
                        break;
                    case 1: // Select part II
                        _playerInfo = FileUtils.GetPlayer("partII.txt");
                        FillPlayerInfo();
                        break;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        public void LoadQuestionFromFile(string filename)
        {
            listBoxQuestion.Items.Clear();
            _questionManagements = FileUtils.LoadQuestionFromFile(filename).ToList();
            foreach (var question in _questionManagements)
            {
                listBoxQuestion.Items.Add(question.Question);
            }

        }
        private void FormControl_KeyDown(object sender, KeyEventArgs e)
        {
        }

        private void WriteToLog(string message)
        {
            txtLog.AppendText(message + "\n");
            txtLog.SelectionStart = txtLog.TextLength;

            //// InvokeRequired required compares the thread ID of the
            //// calling thread to the thread ID of the creating thread.
            //// If these threads are different, it returns true.
            //if (this.txtLog.InvokeRequired)
            //{
            //    var d = new SetTextCallback(WriteToLog);
            //    this.Invoke(d, new object[] { message });
            //}
            //else
            //{
            //    this.txtLog.Text = message;
            //    txtLog.SelectionStart = txtLog.TextLength;
            //}

        }

        delegate void SetTextCallback(string text);

        private void SetText(string text)
        {
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this.txtLog.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetText);
                this.Invoke(d, new object[] { text });
            }
            else
            {
                this.txtLog.Text = text;
            }
        }

        private void txtLog_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode != Keys.L) return;
            if (MessageBox.Show(@"Are you sure to clear log?", this.Text, MessageBoxButtons.YesNo) != DialogResult.Yes)
                return;
            txtLog.Text = "";
            txtLog.SelectionStart = txtLog.TextLength;
        }

        private void listBoxQuestion_SelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                txtQuestionDetails.Text = _questionManagements[listBoxQuestion.SelectedIndex].ToString();
            }
            catch (Exception ex)
            {
                WriteToLog(ex.Message);
            }
        }

        private void btnShowQuestion_Click(object sender, EventArgs e)
        {
            const string msg = @"You are try to publish this question. When you click OK, this question will be automatic sent to all player and display form in projector. However, player will not see this question util you click start button. Please careful with this action.";
            DialogResult dialogResult =
                MessageBox.Show(msg, @"Warrning", MessageBoxButtons.OKCancel);
            if (dialogResult != DialogResult.OK) return;

            var questionMnagement = _questionManagements[listBoxQuestion.SelectedIndex];

            var question = new Question()
            {
                Content = questionMnagement.Question,
                AnsA = questionMnagement.Answers[0],
                AnsB = questionMnagement.Answers[1],
                AnsC = questionMnagement.Answers[2],
                AnsD = questionMnagement.Answers[3]
            };

            switch (questionMnagement.Correct)
            {
                case 0:
                    question.CorrectAnswer = Question.Answer.A;
                    break;
                case 1:
                    question.CorrectAnswer = Question.Answer.B;
                    break;
                case 2:
                    question.CorrectAnswer = Question.Answer.C;
                    break;
                case 3:
                    question.CorrectAnswer = Question.Answer.D;
                    break;
            }

            PassQuestion(question);

            for (var i = 0; i < _ipList.Count; i++)
            {
                var isSuccess = true;
                try
                {
                    if (String.IsNullOrEmpty(_nameList[i].Text)) continue;
                    var temp = _ipList[i].Text.Split(':');
                    SendMessage(temp[0], Int32.Parse(temp[1]), "300:" + _questionManagements[listBoxQuestion.SelectedIndex].EncodeQuestion());
                    //SendMessage(temp[0], int.Parse(temp[1]), "304:" + (listBoxQuestion.SelectedIndex + 1));

                }
                catch (Exception ex)
                {
                    WriteToLog(ex.Message + "\n");
                    isSuccess = false;
                }
                if (isSuccess)
                {
                    WriteToLog(String.Format("Send to {0}: ", _nameList[i].Text));
                }
            }
            txtLog.AppendText("Show Question action done.\n");
        }

        private void SendMessage(string ip, int port, string message)
        {
            try
            {
                var client = new TcpClient(ip, port);
                var sw = new StreamWriter(client.GetStream());
                sw.Write(message);
                sw.Flush();
                sw.Close();
            }
            catch (Exception ex)
            {
                WriteToLog(ex.Message);
            }
        }

        private void SendMessage(string message)
        {
            for (var i = 0; i < _ipList.Count; i++)
            {
                try
                {
                    if (String.IsNullOrEmpty(_nameList[i].Text)) continue;
                    var temp = _ipList[i].Text.Split(':');
                    SendMessage(temp[0], Int32.Parse(temp[1]), message);

                }
                catch (Exception ex)
                {
                    WriteToLog(ex.Message);
                }
            }
        }

        private static void RaiseErrorMessage()
        {
            MessageBox.Show(@"Connect error!!! Please contact administrator, NOW!!!", @"ERROR",
                   MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            try
            {
                if (btnStart.Text.Equals("Start"))
                {
                    if (MessageBox.Show(@"Are you sure to begin test?", this.Text, MessageBoxButtons.YesNo,
                        MessageBoxIcon.Question) != DialogResult.Yes) return;
                    btnStart.Text = @"Stop";

                    PassStart(int.Parse(txtMaxTime.Text));

                    for (var i = 0; i < _marks.Count; i++)
                    {
                        _marks[i] = 0;
                    }
                    ShowHideBonusAction(true);
                    SendMessage(@"301:" + txtMaxTime.Text);
                }
                else if (btnStart.Text.Equals("Stop"))
                {
                    if (MessageBox.Show(@"Are you sure to stop current test?", this.Text, MessageBoxButtons.YesNo,
                        MessageBoxIcon.Question) != DialogResult.Yes) return;
                    btnStart.Text = @"Start";

                    PassStop();

                    SendMessage(@"302:Abort");
                    ShowHideBonusAction(false);
                }
            }
            catch (Exception ex)
            {
                WriteToLog(ex.Message);
            }
        }

        private void ShowHideBonusAction(bool isShow)
        {
            txtBonusTime.Visible = isShow;
            btnBonusTime.Visible = isShow;
        }

        private void btnBonusTime_Click(object sender, EventArgs e)
        {
            if (
                MessageBox.Show(@"Are you sure to give a bonus time?", this.Text, MessageBoxButtons.YesNo,
                    MessageBoxIcon.Question) == DialogResult.Yes)
            {
                PassBonus(int.Parse(txtBonusTime.Text), true);
                SendMessage(String.Format("303:{0}", txtBonusTime.Text));
            }
        }

        private void btnShowAnswer_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show(@"Are you sure to show answer?", this.Text, MessageBoxButtons.YesNo,
                MessageBoxIcon.Question) != DialogResult.Yes) return;

            var inCorrectList = new List<int>();
            for (var i = 0; i < _inCorrectPos.Length; i++)
            {
                if (_inCorrectPos[i] == 1)
                {
                    inCorrectList.Add(i);
                }
            }

            PassInCorrect(inCorrectList.ToArray());
            var ans = 'A';
            switch (_questionManagements[listBoxQuestion.SelectedIndex].Correct)
            {
                case 1:
                    ans = 'B';
                    break;
                case 2:
                    ans = 'C';
                    break;
                case 3:
                    ans = 'D';
                    break;
            }
            PassCorrectAnswer(ans);

            try
            {
                for (var i = 0; i < _marks.Count; i++)
                {
                    if (String.IsNullOrEmpty(_nameList[i].Text)) continue;
                    var temp = _ipList[i].Text.Split(':');
                    //PassMark(i, _markStore[i]);
                    PassData(String.Format("304:{0}:{1}", i, _marks[i]));
                    SendMessage(temp[0], Int32.Parse(temp[1]), String.Format("304:{0}", _marks[i]));
                    _markList[i].Text = string.Format("{0}", int.Parse(_markList[i].Text) + _marks[i]);
                }
            }
            catch (Exception ex)
            {
                WriteToLog(ex.Message);
            }
        }

        private void SetMark(int index)
        {
            try
            {
                if (MessageBox.Show(
                    String.Format("Are you sure to set {0} mark(s) for {1}", _markList[index].Text, _nameList[index].Text),
                    this.Text, MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes) return;
                if (String.IsNullOrEmpty(_nameList[0].Text)) return;
                var temp = _ipList[index].Text.Split(':');
                SendMessage(temp[0], Int32.Parse(temp[1]), String.Format("308:{0}", _markList[index].Text));
                PassData(String.Format("308:{0}:{1}", index, _markList[index].Text));
                //PassMark(index, int.Parse(_markList[index].Text));
            }
            catch (Exception ex)
            {
                WriteToLog(ex.Message);
            }
        }

        private void btnSetMark1_Click(object sender, EventArgs e)
        {
            SetMark(0);
        }

        private void btnSetMark2_Click(object sender, EventArgs e)
        {
            SetMark(1);
        }

        private void btnSetMark3_Click(object sender, EventArgs e)
        {
            SetMark(2);
        }

        private void btnSetMark4_Click(object sender, EventArgs e)
        {
            SetMark(3);
        }

        private void btnSetMark5_Click(object sender, EventArgs e)
        {
            SetMark(4);
        }

        private void btnSetMark6_Click(object sender, EventArgs e)
        {
            SetMark(5);
        }

        private void SetWinner(int index)
        {
            try
            {
                if (MessageBox.Show(
                    String.Format("Are you sure to set {0} to a winner", _nameList[index]),
                    this.Text, MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes) return;
                if (String.IsNullOrEmpty(_nameList[0].Text)) return;
                var temp = _ipList[index].Text.Split(':');
                PassWinnerLoser(index, "win");
                SendMessage(temp[0], Int32.Parse(temp[1]), String.Format("305:Winner"));
            }
            catch (Exception ex)
            {
                WriteToLog(ex.Message);
            }
        }
        private void btnWin1_Click(object sender, EventArgs e)
        {
            SetWinner(0);
        }

        private void btnWin2_Click(object sender, EventArgs e)
        {
            SetWinner(1);
        }

        private void btnWin3_Click(object sender, EventArgs e)
        {
            SetWinner(2);
        }

        private void btnWin4_Click(object sender, EventArgs e)
        {
            SetWinner(3);
        }

        private void btnWin5_Click(object sender, EventArgs e)
        {
            SetWinner(4);
        }

        private void btnWin6_Click(object sender, EventArgs e)
        {
            SetWinner(5);
        }

        private void SetLoser(int index)
        {
            try
            {
                if (MessageBox.Show(
                    String.Format("Are you sure to set {0} to a loser", _nameList[index]),
                    this.Text, MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes) return;
                if (String.IsNullOrEmpty(_nameList[0].Text)) return;
                var temp = _ipList[index].Text.Split(':');
                PassWinnerLoser(index, "lose");
                SendMessage(temp[0], Int32.Parse(temp[1]), String.Format("306:Loser"));
            }
            catch (Exception ex)
            {
                WriteToLog(ex.Message);
            }
        }

        private void btnLose1_Click(object sender, EventArgs e)
        {
            SetLoser(0);
        }

        private void btnLose2_Click(object sender, EventArgs e)
        {
            SetLoser(1);
        }

        private void btnLose3_Click(object sender, EventArgs e)
        {
            SetLoser(2);
        }

        private void btnLose4_Click(object sender, EventArgs e)
        {
            SetLoser(3);
        }

        private void btnLose5_Click(object sender, EventArgs e)
        {
            SetLoser(4);
        }

        private void btnLose6_Click(object sender, EventArgs e)
        {
            SetLoser(5);
        }

        private void btnShowWatiingScreen_Click(object sender, EventArgs e)
        {

        }

        //private void btnBegin_Click(object sender, EventArgs e)
        //{
        //    if (
        //        MessageBox.Show(@"Are you sure to begin B plan?", this.Text, MessageBoxButtons.YesNo,
        //            MessageBoxIcon.Question) != DialogResult.Yes) return;
        //    try
        //    {
        //        var timeSpane = TimeSpan.Parse(txtTime.Text);
        //        SendMessage(string.Format("307:{0}", timeSpane));
        //        WriteToLog(string.Format("Send {0} time to avaible player.", txtTime.Text));
        //    }
        //    catch (Exception ex)
        //    {
        //        WriteToLog(ex.Message);
        //    }
        //}
    }
}
